package com.duobaoyu.dby.supply.biz.service.config.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.duobaoyu.dby.common.core.constant.CommonConstants;
import com.duobaoyu.dby.common.mybatis.support.ParamConvert;
import com.duobaoyu.dby.supply.biz.constant.SupplyChainConstants;
import com.duobaoyu.dby.supply.biz.converter.OperationLogConverter;
import com.duobaoyu.dby.supply.biz.service.config.OperationLogService;
import com.duobaoyu.dby.supply.common.biz.bo.config.OperationLogBO;
import com.duobaoyu.dby.supply.common.biz.dto.config.OperationLogListDTO;
import com.duobaoyu.dby.supply.common.biz.dto.config.OperationLogPageQueryDTO;
import com.duobaoyu.dby.supply.common.biz.dto.config.OperationLogSaveDTO;
import com.duobaoyu.dby.supply.db.entity.config.OperationLog;
import com.duobaoyu.dby.supply.db.repository.order.OperationLogRepository;
import com.duobaoyu.dby.supply.db.util.OperateAuthUtil;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.Diff;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * @author chenms
 * @date 2023/10/21 2:25 PM
 */
@Service
@RequiredArgsConstructor
public class OperationLogServiceImpl implements OperationLogService {

    private final OperationLogRepository operationLogRepository;
    private final OperationLogConverter operationLogConverter;
    private final ThreadPoolExecutor supplyChainThreadPool;

    @Override
    public boolean save(OperationLogSaveDTO operationLogSave) {
        String operator = Optional.ofNullable(operationLogSave.getOperator()).orElse(OperateAuthUtil.getAdministrator());
        return operationLogRepository.save(this.convertToOperationLog(operationLogSave, operator));
    }

    @Override
    public void asyncSave(OperationLogSaveDTO operationLogSave) {
        String operator = Optional.ofNullable(operationLogSave.getOperator()).orElse(OperateAuthUtil.getAdministrator());
        supplyChainThreadPool.execute(()->{
            operationLogRepository.save(this.convertToOperationLog(operationLogSave, operator));
        });
    }

    @Override
    public void asyncBatchSave(List<OperationLogSaveDTO> operationLogSaveList) {
        String administrator = OperateAuthUtil.getAdministrator();
        supplyChainThreadPool.execute(()->{
            List<OperationLog> operationLogList = operationLogSaveList.stream().map(operationLogSave -> {
                return this.convertToOperationLog(operationLogSave, administrator);
            }).collect(Collectors.toList());
            operationLogRepository.saveBatch(operationLogList);
        });
    }

    @Override
    public Page<OperationLogBO> queryPage(OperationLogPageQueryDTO operationLogPageQuery) {
        // 创建条件对象
        Criteria criteria = new Criteria();
        if (StringUtils.isNotBlank(operationLogPageQuery.getBizKey())) {
            criteria.and("biz_key").is(operationLogPageQuery.getBizKey());
        }
        if (StringUtils.isNotBlank(operationLogPageQuery.getLogType())) {
            criteria.and("log_type").is(operationLogPageQuery.getLogType());
        }
        Page<OperationLog> logPage = operationLogRepository.page(ParamConvert.getPage(operationLogPageQuery), Query.query(criteria));
        return operationLogConverter.toOperationLogPage(logPage);
    }

    @Override
    public List<OperationLogBO> list(OperationLogListDTO operationLogList) {
        // 创建条件对象
        Criteria criteria = new Criteria();
        if (StringUtils.isNotBlank(operationLogList.getBizKey())) {
            criteria.and("biz_key").is(operationLogList.getBizKey());
        }
        if (StringUtils.isNotBlank(operationLogList.getLogType())) {
            criteria.and("log_type").is(operationLogList.getLogType());
        }
        List<OperationLog> logList = operationLogRepository.list(Query.query(criteria)
                .limit(SupplyChainConstants.MAX_LIST_QUERY_COUNT_LIMIT)
                .skip(Optional.ofNullable(operationLogList.getOffset()).orElse(CommonConstants.ZERO)));
        return operationLogConverter.toOperationLogList(logList);
    }

    /**
     * 获取变动内容
     */
    private List<OperationLog.ChangeItem> toChangeItem(List<? extends Diff> diffList) {
        if (CollectionUtils.isEmpty(diffList)) {
            return Collections.emptyList();
        }
        return diffList.stream().map(diff -> {
            OperationLog.ChangeItem changeItem = new OperationLog.ChangeItem();
            changeItem.setField(diff.getFieldName());
            changeItem.setOldValue(diff.getLeft());
            changeItem.setNewValue(diff.getRight());
            return changeItem;
        }).collect(Collectors.toList());
    }

    private OperationLog convertToOperationLog(OperationLogSaveDTO operationLogSave, String operator) {
        OperationLog operationLog = new OperationLog();
        operationLog.setOperator(operator);
        operationLog.setScene(operationLogSave.getScene());
        operationLog.setBizKey(operationLogSave.getBizKey());
        operationLog.setLogType(operationLogSave.getLogType());
        operationLog.setSource(operationLogSave.getSource());
        operationLog.setCreateTime(LocalDateTime.now());
        operationLog.setChangeItemList(toChangeItem(operationLogSave.getDiffList()));
        return operationLog;
    }

}
